/*
 * 业务接口模块
 *
 * 实现各种网络请求对应的处理函数
 * 包括：文件搜索，文件记录，文件上传，文件下载
 *
 **/

#pragma once

#include "util.hh"
#include <httplib.h>
#include <easylog.h>
#include "config.hh"
#include "info.hh"
#include "data.hh"

namespace cloud
{
struct business
{
    // For search request,
    // parse the filename in url, search and return JSON of that file's info.
    // if not exists, the fpath will be invalid download url.
    static void search(const httplib::Request& req, httplib::Response& rsp)
    {
        backup_info* info = data_manager::instance()
            .get_by_fpath(util::path::dlurl2fpath(req.path));

        if (!info)
        {
            backup_info tmp;
            tmp.fpath = "invalid download url";

            util::json js;
            js.append(tmp.parser_to_json());

            rsp.body = js.serialize();
            rsp.status = 200;
            rsp.set_header("Content-Type", "application/json");

            easylog::error("handle an search request failed");
        }
        else
        {
            util::json js;
            js.append(info->parser_to_json());

            rsp.body = js.serialize();
            rsp.status = 200;
            rsp.set_header("Content-Type", "application/json");

            easylog::info("handle an search request succ");
        }
    }

    // For record request,
    // return json of infos about all files
    static void record(const httplib::Request& req, httplib::Response& rsp)
    {
        std::vector<backup_info> infos = data_manager::instance().get_all();

        util::json js;
        for (auto& info : infos)
             js.append(info.parser_to_json());

        rsp.body = js.serialize();
        rsp.status = 200;
        rsp.set_header("Content-Type", "application/json");

        easylog::info("handle an record request succ");
    }

    // For upload request
    static void upload(const httplib::Request& req, httplib::Response& rsp)
    {
        util::json js;

        rsp.status = 200;
        rsp.set_header("Content-Type", "application/json");

        if (!req.has_file("file"))
        {
            // return failure
            rsp.body = js.set("result", false).serialize();

            easylog::warn("upload request error with no file");
        }
        else
        {
            // Generate the file in the back dir, and add its info to the hash table.
            auto file = req.get_file_value("file");

            util::file(util::path::fpath2bpath(file.filename))
                .write(file.content);
            data_manager::instance().insert(backup_info(file.filename));

            // return success
            rsp.body = js.set("result", true).serialize();

            easylog::info("handle a upload request succ, {}", file.filename);
        }
    }

    // For download request
    static void download(const httplib::Request& req, httplib::Response& rsp)
    {
        backup_info* info =
            data_manager::instance().get_by_url(req.path);

        // if the file is not exists, return 404 page
        if (!info)
        {
            rsp.status = 404;
            rsp.set_header("Content-Type", "text/html");
            rsp.body = util::file(_404_page()).read();

            easylog::warn("handle a download request failed, {}", req.path);
            return;
        }

        if (req.has_header("If-Range") && req.get_header_value("If-Range") == info->etag()) // 断点续传
        {
            rsp.status = 206; // partial content
            // rsp.set_header("Content-Ranges", "bytes start-end/fsize");
        }
        else // if the file is exists, return the content stream
        {
            rsp.status = 200;
        }

        easylog::info("handle a download request succ");

        rsp.set_header("ETag", info->etag());
        rsp.set_header("Accept-Ranges",  "bytes");
        rsp.set_header("Content-Type",   "application/octet-stream");
        rsp.set_header("Content-Disposition", "attachment; filename=\"" + info->fpath + "\""); // 服务器向浏览器发送文件时，如果是浏览器支持的文件类型，一般会默认使用浏览器打开，比如txt、jpg等。如果需要提示用户保存，就要利用Content-Disposition进行处理，
        
        rsp.body = info->content(); // httplib会自动截取
    }

    // for filelist request
    // return the filelist web page, also is the index page
    static void filelist(const httplib::Request& req, httplib::Response& rsp)
    {
        util::file page = (_filelist_page());

        if (!page.exists())
        {
            rsp.body =
                util::file(_404_page()).read();
            rsp.status = 404;
            rsp.set_header("Content-Type", "text/html");

            easylog::warn("handle a filelist request error");
        }
        else
        {
            rsp.body = page.read();
            rsp.status = 200;
            rsp.set_header("Content-Type", "text/html");

            easylog::info("handle a filelist request succ");
        }
    }

    static void webserver(const httplib::Request& req, httplib::Response& rsp)
    {
        std::string path = config::instance().web_root_dir() + util::path::bpath2fpath(req.path);

        if (path.back() == '/')
            path += "index.html";

        if (!util::file(path).exists())
        {
            std::string page = _404_page();
            rsp.status = 404;
            rsp.body = util::file(page).read();
            rsp.set_header("Content-Type", "text/html");
        }
        else
        {
            rsp.status = 200;
            rsp.body = util::file(path).read();
            rsp.set_header("Content-Type", "text/html");
        }
    }

private:
    static std::string _404_page()
    {
        static std::string path = config::instance().web_root_dir() + "404.html";
        return path;
    }
    static std::string _filelist_page()
    {
        static std::string path = config::instance().web_root_dir() + "filelist.html";
        return path;
    }
    static std::string _upload_page()
    {
        static std::string path = config::instance().web_root_dir() + "upload.html";
        return path;
    }
};
}
